/******************************************************************************
This file is part of AppKit.
Project: appkit
Author : FergusZeng
Email  : cblock@126.com
git	   : https://gitee.com/newgolo/appkit.git
*******************************************************************************
MIT License

Copyright (c) 2022 cblock@126.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************************************************************/
#pragma once

// cygwin和linux下只能使用sysv的消息队列,Android下使用posix消息队列
#if USE_SYSV
#include <sys/ipc.h>
#include <sys/msg.h>
#else
#include <fcntl.h>
#include <mqueue.h>
#include <sys/stat.h>
#endif
#include <sys/types.h>

#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "appkit/basetype.h"
#include "appkit/fileio.h"
#include "appkit/singleton.h"
#include "appkit/thread.h"

#define MSG_SIZE_MAX 128         /**< 最大消息长度 */
#define POSIX_MQFS "/dev/mqueue" /**< Posix消息队列挂载点 */
/**
 * @file ipc.h
 * @brief 进程间通信
 */
namespace appkit {

/**
 *  @struct QueueMsgBody
 *  @brief  msgbuf消息结构体
 */
struct QueueMsgBody {
    int m_msgType;                 /**< 消息类型 */
    unsigned char m_dataLen;       /**< 数据长度 */
    char m_data[MSG_SIZE_MAX - 1]; /**< 消息数据 */
};

/**
 *  @class  MsgQueue
 *  @brief  消息队列类
 */
class MsgQueue {
    DECL_CLASSMETA(MsgQueue)

public:
    explicit MsgQueue(key_t key);
    virtual ~MsgQueue();
    /**
     *  @brief  初始化消息队列
     *  @param  void
     *  @return true
     *  @return false
     */
    bool initialize();
    /**
     *  @brief  发送消息
     *  @param  msg 消息体
     *  @return 成功返回RC_OK,失败返回RC_ERROR
     */
    int sendMsg(const QueueMsgBody& msg);
    /**
     *  @brief  接收消息
     *  @param  msg 消息体
     *  @param  msgType 消息类型
     *  @return 成功返回RC_OK,失败返回RC_ERROR
     */
    int recvMsg(const QueueMsgBody& msg, int msgType = 0);
    /**
     *  @brief  清空消息
     *  @param  msgType 消息体
     *  @return 成功返回STATUS_OK,失败返回STATUS_ERROR
     *  @note   msgType=0 清空队列中所有的消息
     *          msgType>0 清空队列中类型值为msgType的消息
     *          msgType<0 清空队列中类型值小于或等于|msgType|的消息
     */
    int clearMsg(int msgType = 0);

private:
    std::string getMqName(int key);

private:
    int m_msgID{-1};
    key_t m_key{-1};
};

/**
 *  @class  MsgQueueFactory
 *  @brief  消息队列工厂类
 */
class MsgQueueFactory : public Singleton<MsgQueueFactory> {
    DECL_CLASSMETA(MsgQueueFactory)
    DECL_SINGLETON(MsgQueueFactory)
public:
    ~MsgQueueFactory();
    /**
     * @brief 从消息队列工厂取得消息队列
     * @param key
     * @return std::shared_ptr<MsgQueue>
     */
    std::shared_ptr<MsgQueue> getMsgQueue(key_t key);

private:
    std::mutex m_mutex;
    std::map<key_t, std::shared_ptr<MsgQueue>> m_msgQueueMap;
};

/**
 * @class MemPool
 * @brief 内存池
 */
class MemPool {
    DECL_CLASSMETA(MemPool)

public:
    MemPool();
    ~MemPool();
    /**
     * @brief 初始化内存池
     * @param blockNum
     * @param blockSize
     * @param memStart 如果指定了memStart则在memStart内存地址处创建内存池
     * @return true
     * @return false
     */
    bool init(int blockNum, int blockSize, void* memStart = nullptr);
    /**
     * @brief 申请内存
     * @param memoryName 内存名称
     * @param memorySize 内存大小
     * @return void*
     */
    void* getMemory(const std::string& memoryName, int memorySize);
    /**
     * @brief 释放内存
     * @param memoryName 内存名称
     * @return true
     * @return false
     */
    bool putMemory(const std::string& memoryName);
    /**
     * @brief 显示内存
     *
     */
    void showMemory();

private:
    struct MemBlock {
        bool isUsed{false};
        void* memStart{nullptr};
        std::string memName{""};
    };
    int m_blockNum{0};
    int m_blockSize{0};
    void* m_poolStart{nullptr};
    std::vector<std::unique_ptr<MemBlock>> m_memBlocks;
    std::mutex m_poolMutex;
};

/**
 * @class Fifo
 * @brief 进程间管道(有名管道)
 */
class Fifo : public IODevice {
    DECL_CLASSMETA(Fifo)
public:
    Fifo();
    virtual ~Fifo();
    /**
     * @brief 打开有名管道
     * @param devName 管道名称
     * @param ioMode 设备模式(IO_MODE_E)
     * @return true 打开成功
     * @return false 打开失败
     */
    virtual bool open(const std::string& devName,
                      int ioMode = IO_MODE_RDWR_ONLY);
};

/**
 *  @class  MemShared
 *  @brief  共享内存类(可以多进程共享)
 */
class MemShared {
    DECL_CLASSMETA(MemShared)

public:
    enum Type { SHM_MMAP = 0, FILE_MMAP };

public:
    MemShared();
    virtual ~MemShared();
    /**
     * @brief 打开共享内存
     * @param name 共享内存名称
     * @param size 共享内存大小
     * @param mode IO_MODE_E,仅支持部分模式
     * @param type MemShared::Type
     * @return true
     * @return false
     */
    bool open(std::string name, int size, int mode,
              MemShared::Type type = SHM_MMAP);
    /**
     * @brief 关闭共享内存
     * @return true
     * @return false
     */
    bool close();
    /**
     * @brief 放置数据到内存
     * @param data
     * @param size
     * @return int
     */
    int putData(void* data, int size);
    /**
     * @brief 获取内存数据
     * @param buffer
     * @param size
     * @return int
     */
    int getData(void* buffer, int size);
    /**
     * @brief 获取内存大小
     * @return int
     */
    int size();
    /**
     * @brief 获取内存地址
     * @return void*
     */
    void* data();

    /**
     * @brief 返回文件描述符
     * @return int
     */
    int fd() { return m_fd; }

private:
    MemShared::Type m_type{SHM_MMAP};
    int m_fd{-1};
    int m_size{0};
    void* m_memStart{nullptr};
    std::string m_name{""};
    std::shared_ptr<Semaphore> m_procSem{nullptr};
};
}  // namespace appkit
